﻿using System;
using System.Configuration;
using System.Runtime.InteropServices;

namespace Neovolve.Extensibility.VisualStudio.ReflectorLink
{
    /// <summary>
    /// 
    /// </summary>
    /// <remarks>
    /// This code was modelled of the RemoteController class in the Hawkeye project developed by Corneliu I. Tusnea. 
    /// See <a href="http://www.codeplex.com/hawkeye/SourceControl/FileView.aspx?itemId=139185&changeSetId=6535" target="_blank">here</a>.
    /// </remarks>
    internal static class ReflectorController
    {
        /// <summary>
        /// The configuration key used to define the title of the Reflector window.
        /// </summary>
        public const String ReflectorWindowTitleKey = "ReflectorWindowTitle";

        public static Boolean LoadAssembly(String fileName)
        {
            return Send("LoadAssembly\n" + fileName);
        }

        public static Boolean SelectAssemblyDeclaration(String key)
        {
            return Send("SelectAssemblyDeclaration\n" + key);
        }

        public static Boolean SelectEventDeclaration(String key)
        {
            return Send("SelectEventDeclaration\n" + key);
        }

        public static Boolean SelectFieldDeclaration(String key)
        {
            return Send("SelectFieldDeclaration\n" + key);
        }

        public static Boolean SelectMethodDeclaration(String key)
        {
            return Send("SelectMethodDeclaration\n" + key);
        }

        public static Boolean SelectNamespaceDeclaration(String key)
        {
            return Send("SelectNamespaceDeclaration\n" + key);
        }

        public static Boolean SelectPropertyDeclaration(String key)
        {
            return Send("SelectPropertyDeclaration\n" + key);
        }

        public static Boolean SelectTypeDeclaration(String key)
        {
            return Send("SelectTypeDeclaration\n" + key);
        }

        public static Boolean UnloadAssembly(String fileName)
        {
            return Send("UnloadAssembly\n" + fileName);
        }

        private static IntPtr GetHandle()
        {
            String title = ConfigurationManager.AppSettings[ReflectorWindowTitleKey];
            
            IntPtr hwnd = IntPtr.Zero;

            if (String.IsNullOrEmpty(title) == false)
            {
                hwnd = FindWindow(null, title);
            }

            if (hwnd == IntPtr.Zero)
            {
                const String RedGateTitle = "Red Gate's .NET Reflector";

                hwnd = FindWindow(null, RedGateTitle);
            }

            if (hwnd == IntPtr.Zero)
            {
                const String LutzTitle = "Lutz Roeder's .NET Reflector";

                hwnd = FindWindow(null, LutzTitle);
            }

            return hwnd;
        }

        private static Boolean Send(String message)
        {
            IntPtr hWnd = GetHandle();

            if (hWnd != IntPtr.Zero)
            {
                char[] source = message.ToCharArray();
                CopyDataStruct lParam = new CopyDataStruct();

                lParam.Padding = IntPtr.Zero;
                lParam.Size = source.Length * 2;
                lParam.Buffer = Marshal.AllocHGlobal(lParam.Size);

                Marshal.Copy(source, 0, lParam.Buffer, source.Length);

                const Int32 WM_COPYDATA = 0x4a;
                bool flag = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ref lParam);

                Marshal.FreeHGlobal(lParam.Buffer);

                return flag;
            }

            return false;
        }

        /// <summary>
        /// Sets the focus.
        /// </summary>
        /// <returns></returns>
        public static Boolean SetFocus()
        {
            IntPtr hwnd = GetHandle();

            // Check if the application is running
            if (hwnd == IntPtr.Zero)
            {
                return false;
            }

            // Check if the window is minimized
            if (IsIconic(hwnd))
            {
                // Restore the window
                ShowWindow(hwnd, SW_RESTORE);
            }

            // Set the window as active and in the foregound
            return SetForegroundWindow(hwnd);
        }

        /// <summary>
        /// Gets a value indicating whether this instance is running.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is running; otherwise, <c>false</c>.
        /// </value>
        public static Boolean IsRunning
        {
            get
            {
                return GetHandle() != IntPtr.Zero;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is supported.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is supported; otherwise, <c>false</c>.
        /// </value>
        public static Boolean IsSupported
        {
            get
            {
                return Send("Available\n4.0.0.0");
            }
        }

        /// <summary>
        /// Activates and displays the window. If the window is 
        /// minimized or maximized, the system restores it to its original size 
        /// and position. An application should specify this flag when restoring 
        /// a minimized window.
        /// </summary>
        private const Int32 SW_RESTORE = 9;

        [DllImport("user32.dll")]
        private static extern IntPtr FindWindow(string className, string windowName);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool IsIconic(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref CopyDataStruct lParam);
        
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        
        [DllImport("user32.dll")]
        public static extern int ShowWindow(IntPtr Hwnd, int iCmdShow);

        [StructLayout(LayoutKind.Sequential)]
        private struct CopyDataStruct
        {
            public IntPtr Padding;
            public int Size;
            public IntPtr Buffer;
        }
    }
}